﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using TradeSeaShared;

using System.Threading;
using System.Reflection;
using System.Diagnostics;

using Lidgren.Network;

namespace TradeSea
{
    public class ServerBoat {
        public int id;
        public float rudder = 0;
        public float yard = 0;

        public ServerBoat(int id)
        {
            this.id = id;
        }

        public void Update(float rudder, float yard) {
            this.rudder = rudder;
            this.yard = yard;
        }
    }

    public static class ServerBoats
    {
        public static Dictionary<NetConnection, ServerBoat> Boats = new Dictionary<NetConnection,ServerBoat>();

        public static void UpdateBoat(float rudder, float yard, NetConnection conn)
        {
            if (!Boats.ContainsKey(conn))
            {
                Boats.Add(conn, new ServerBoat(conn.RemoteEndpoint.Port));
            }
            Boats[conn].Update(rudder, yard);
        }

        public static int GetCount()
        {
            return Boats.Count;
        }
    }

    public class TradeSeaServer : INet
    {
        private NetServer Server;
        private Dictionary<int, MethodInfo> PacketHandles = new Dictionary<int, MethodInfo>();

        private static Random rand = new Random();
        private int worldSeed = rand.Next(Int32.MaxValue);

        Stopwatch allClientUpdate = new Stopwatch();

        public static void RunServer()
        {
            TradeSeaServer server = new TradeSeaServer();
            // If it's a listen sevrer
            if (Singiltons.IsListen)
            {
                Thread thr = new Thread(new ThreadStart(server.Run));
                thr.IsBackground = true;
                thr.Start();
            }
            else
            {
                server.Run();
            }
        }

        public void Run()
        {
            Server = new NetServer(Singiltons.ServerNetConfig);

            foreach (MethodInfo item in new ServerPackets().GetType().GetMethods(BindingFlags.Static | BindingFlags.Public))
            {
                this.PacketHandles.Add(((PacketAttribute)(item.GetCustomAttributes(false)[0])).ID, item);
            }

            Server.Start();
            allClientUpdate.Start();
            Singiltons.Log("Server Network", "Server Starting");
            while (true)
            {
                NetIncomingMessage msg = this.Server.ReadMessage();
                if (msg == null)
                {
                    Thread.Sleep(0);
                    if (allClientUpdate.ElapsedMilliseconds > 2000)
                    {
                        SendAllClientUpdate();
                        allClientUpdate.Restart();
                    }
                    continue;
                }
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.StatusChanged:
                        Singiltons.Log("Server Network", "Client : " + msg.SenderEndpoint.ToString() + " : Status Changed to : " + msg.SenderConnection.Status.ToString());
                        break;
                    case NetIncomingMessageType.Data:
                        {
                            int id = msg.ReadInt32();
                            if (PacketHandles.ContainsKey(id))
                            {
                                PacketHandles[id].Invoke(null, new object[] { msg, this });
                            }
                        }
                        break;
                    case NetIncomingMessageType.ConnectionApproval:
                        NetOutgoingMessage hail = Server.CreateMessage();
                        hail.Write(worldSeed);
                        msg.SenderConnection.Approve(hail);
                        break;
                    default:
                        Singiltons.Log("Server Network", "Packet Not Handled : " + msg.MessageType.ToString());
                        break;
                }

                Thread.Sleep(0); // This gives the operating system some time, 
                // other wise this would eat a entire core for no reason
            }
        }

        private void SendAllClientUpdate()
        {
            NetOutgoingMessage msg = CreateMessage(2);
            msg.Write(ServerBoats.GetCount());
            foreach (ServerBoat boat in ServerBoats.Boats.Values)
            {
                msg.Write(boat.rudder);
                msg.Write(boat.yard);
                msg.Write(boat.id);
            }
            SendMessageToAll(msg);
        }

        public void SendMessage(NetOutgoingMessage message, NetConnection to)
        {
            this.Server.SendMessage(message, to, NetDeliveryMethod.ReliableOrdered);
        }

        public NetOutgoingMessage CreateMessage(int id)
        {
            NetOutgoingMessage ret = this.Server.CreateMessage();
            ret.Write(id);
            return ret;
        }

        public void SendMessageToAll(NetOutgoingMessage message)
        {
            this.Server.SendToAll(message, NetDeliveryMethod.ReliableOrdered);
        }
    }
}
